package com.xxmw.transaction.service.app.component;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xxmw.transaction.common.utils.DeflateUtil;
import com.xxmw.transaction.enumeration.BourseEnum;
import com.xxmw.transaction.model.app.BoursePrice;
import com.xxmw.transaction.model.app.Symbol;
import com.xxmw.transaction.service.app.SymbolService;
import com.xxmw.transaction.service.support.AbstractWebSocketAdapter;
import com.xxmw.transaction.service.support.SymbolSubWebSocket;
import lombok.extern.slf4j.Slf4j;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.handshake.ServerHandshake;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * 久币网 websocket api
 * https://www.99ex.com/docs/zh/#spot_ws-order
 * @author luowenliang
 * @since 2020/3/26 16:07
 */
@Slf4j
@Component
public final class JiuBiApplication extends AbstractWebSocketAdapter implements SymbolSubWebSocket {

    private static final String WS_URL = "wss://real.okex.com:8443/ws/v3?brokerId=108";

    private static final String CHANNEL = "{\"op\": \"subscribe\", \"args\": [\"spot/ticker:%s\"]}";

    @Autowired
    private SymbolService symbolService;


    /**
     * 执行心跳的间隔时间和在此基础商上的超时时间将由实现的子类所设定(或者以配置的方式来是定)
     * 假如：此时间有父类适配器所设定那么所有共同子类将继承，如需改变其中一个子类的时间，将会造成
     * 所有子类间隔时间更改
     */
    private long period = 3000;

    @Override
    public BoursePrice getSymbolPrice(String symbol) {
        return symbol_map.get(symbol);
    }

    @Override
    public void cleanSymbolPrice(String symbol) {
        symbol_map.remove(symbol);
    }

    /**
     * {"op": "<value>", "args": ["<value1>","<value2>"]}
     * 其中 op 的取值为 1--subscribe 订阅； 2-- unsubscribe 取消订阅 ；3--login 登录
     * args: 取值为频道名，可以定义一个或者多个频道
     * @param symbol
     */
    @Override
    public void subscribe(Symbol symbol) {
        this.client.send(String.format(CHANNEL, BourseEnum.OKEX.getSymbolFormat().symbolFormat(symbol)));
    }

    @Override
    public void unSubscribe(Symbol symbol) {
        symbol_map.remove(symbol);
    }

    @Override
    public void subscribeList(Set<Symbol> subSet) {
        for (Symbol symbol: subSet) {
            subscribe(symbol);
        }
    }

    @Override
    public void destroy() {
        this.client.close();
        scheduled.shutdown();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        super.afterPropertiesSet(new JiuBiApplication.JiuBiWebSocketClient(new URI(WS_URL), new Draft_6455(), null, 5000));
        scheduled.scheduleWithFixedDelay(() -> heartBeat(), period, period, TimeUnit.MILLISECONDS);
    }


    private final class JiuBiWebSocketClient extends WebSocketClient {

        public JiuBiWebSocketClient(URI serverUri, Draft protocolDraft, Map<String, String> httpHeaders, int connectTimeout) {
            super(serverUri, protocolDraft, httpHeaders, connectTimeout);
        }

        @Override
        public void onOpen(ServerHandshake serverHandshake) {
            log.info("久币网开启连接");
            Set<Symbol> set = symbolService.listByBourse(BourseEnum.JIUBI.getValue());
            subscribeList(set);
        }

        @Override
        public void onMessage(ByteBuffer bytes) {
            byte[] array = bytes.array();
            String str = DeflateUtil.decompress(array);
            // log.info("久币接收到数据:{}", str);
            if (str.contains("data")) {
                JSONObject object = JSON.parseObject(str);
                JSONObject data = object.getJSONArray("data").getJSONObject(0);
                String symbol = data.getString("instrument_id");

                BigDecimal ask =  data.getBigDecimal("best_ask");
                BigDecimal bid =  data.getBigDecimal("best_bid");
//                log.info("久币网:{}, 卖一价:{}, 买一价:{}", symbol, ask, bid);
                BoursePrice price = BoursePrice.builder().buyPrice(bid).sellPrice(ask).data(data.toJSONString()).updateTime(new Date()).build();
                // log.info("久币网获取到【***{}***】的实时价格为：{}", symbol, price.toString());
                symbol_map.put(BourseEnum.OKEX.getSymbolFormat().symbolStandardization(symbol), price);
            }
        }

        @Override
        public void onMessage(String s) {
            log.info("久币接收到数据:{}",s);
        }

        @Override
        public void onClose(int i, String s, boolean b) {
            log.info("久币网Websocket接口已关闭, 原因: {}, 状态: {}", s, i);
        }

        @Override
        public void onError(Exception e) {
            log.error("久币网WebSocket接口，发生异常", e);
        }
    }
}
